home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / elm / elm2.4 / src / hdrconfg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-21  |  17.9 KB  |  710 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: hdrconfg.c,v 5.2 1992/11/22 01:15:15 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 5.2 $   $State: Exp $
  6.  *
  7.  *            Copyright (c) 1988-1992 USENET Community Trust
  8.  *            Copyright (c) 1986,1987 Dave Taylor
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log: hdrconfg.c,v $
  17.  * Revision 5.2  1992/11/22  01:15:15  syd
  18.  * Add on initial display or display where the entire screen is being
  19.  * drawn, we should not output the trailing blanks that clear the old
  20.  * value.
  21.  * From: chip@chinacat.unicom.com (Chip Rosenthal)
  22.  *
  23.  * Revision 5.1  1992/10/03  22:58:40  syd
  24.  * Initial checkin as of 2.4 Release at PL0
  25.  *
  26.  *
  27.  ******************************************************************************/
  28.  
  29. /**   This file contains the routines necessary to be able to modify
  30.       the mail headers of messages on the way off the machine.  The
  31.       headers currently supported for modification are:
  32.  
  33.     Subject:
  34.     To:
  35.     Cc:
  36.     Bcc:
  37.     Reply-To:
  38.     Expires:
  39.     Priority:
  40.     Precedence:
  41.     In-Reply-To:
  42.     Action:
  43.  
  44.     <user defined>
  45. **/
  46.  
  47. #include "headers.h"
  48. #include "s_elm.h"
  49.  
  50. #include <ctype.h>
  51.  
  52. #ifdef BSD
  53. #undef toupper
  54. #undef tolower
  55. #endif
  56.  
  57. /*
  58.  * Placement of prompts and messages at the bottom of the screen.
  59.  */
  60. #define INSTRUCT_LINE           (LINES-4)
  61. #define INPUT_LINE        (LINES-2)
  62. #define ERROR_LINE        (LINES-1)
  63. #define TOPMOST_PROMPTAREA_LINE INSTRUCT_LINE
  64.  
  65. /*
  66.  * Option flags for the fields in a (struct hdr_menu_item).
  67.  */
  68. #define HF_DISP_1ROW    0001    /* field is displayed on one line    */
  69. #define HF_DISP_2ROW    0002    /* field display spans two lines    */
  70. #define HF_DISP_3ROW    0003    /* field display spans three lines    */
  71. #define HF_DISP_LEFT    0004    /* field occupies left half of a line    */
  72. #define HF_DISP_RIGHT    0005    /* field occupies right half of a line    */
  73. #define HF_DISP_MASK    0007    /* -- mask to pull out display option    */
  74. #define HF_PROMPT_EXP    0010    /* prompt for expires data entry    */
  75. #define HF_PROMPT_USR    0020    /* prompt for user defined hdr entry    */
  76. #define HF_PROMPT_MASK    0070    /* -- mask to pull out prompt option    */
  77. #define HF_APPENDENTRY    0100    /* append user entry to existing value    */
  78.  
  79. /*
  80.  * Structure to describe a header which can be edited in this menu.
  81.  */
  82. struct hdr_menu_item {
  83.     int menucmd;    /* The single keystroke (lower-case letter) the    */
  84.             /*   user strikes to edit this menu item.    */
  85.     char *hdrname;    /* Header name to display in the menu.  Parens    */
  86.             /*   should be used to bracket the "menucmd"    */
  87.             /*   char in the name, e.g. "S)ubject".  This    */
  88.             /*   will be NULL for the user-defined header.    */
  89.     int lineno;        /* Screen line at which the field is displayed.    */
  90.     int flags;        /* Various flags which effect the display and    */
  91.             /*   user entry of this item.            */
  92.     char *inpval;    /* Pointer to the buffer to hold the value    */
  93.             /*   entered by the user.            */
  94.     char *expval;    /* Pointer to the expanded header value to    */
  95.             /*   display.  If no special expansions are    */
  96.             /*   required then this will point to "inpval".    */
  97.     int (*hdrproc)();    /* Pointer to a procedure which verifies the    */
  98.             /*   user data entry, and if required converts    */
  99.             /*   the "inpval" value to "expval" value.  If    */
  100.             /*   no verification or expansion is needed    */
  101.             /*   then this will be NULL.            */
  102. };
  103.  
  104. /*
  105.  * These are all defined in the mailmsg file.
  106.  */
  107. extern char subject[SLEN], in_reply_to[SLEN], expires[SLEN],
  108.             action[SLEN], priority[SLEN], reply_to[SLEN], to[VERY_LONG_STRING],
  109.         cc[VERY_LONG_STRING], expanded_to[VERY_LONG_STRING],
  110.         expanded_reply_to[LONG_STRING],
  111.         expanded_cc[VERY_LONG_STRING], user_defined_header[SLEN],
  112.         bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING],
  113.         precedence[SLEN], expires_days[SLEN];
  114.  
  115. /*
  116.  * Local procedures.
  117.  */
  118. static void hdrmenu_clear_promptarea();
  119. static int hdrmenu_get();
  120. static void hdrmenu_put();
  121. static int hdrproc_addr();
  122. static int hdrproc_expires();
  123. static int hdrproc_precedence();
  124. static int hdrproc_userhdr();
  125. static int domainize_submenu();
  126. static void domainize();
  127. static void domainize_addr();
  128.  
  129. /*
  130.  * Definition of all the header editing menu fields.
  131.  */
  132. struct hdr_menu_item hmenu_item_list[] = {
  133.     { 't', "T)o",         2, HF_DISP_3ROW|HF_APPENDENTRY,
  134.                     to, expanded_to, hdrproc_addr },
  135.     { 'c', "C)c",         5, HF_DISP_3ROW|HF_APPENDENTRY,
  136.                     cc, expanded_cc, hdrproc_addr },
  137.     { 'b', "B)cc",         8, HF_DISP_2ROW|HF_APPENDENTRY,
  138.                     bcc, expanded_bcc, hdrproc_addr },
  139.     { 's', "S)ubject",        10, HF_DISP_2ROW, subject, subject, NULL },
  140.     { 'r', "R)eply-to",        12, HF_DISP_1ROW, reply_to, expanded_reply_to,
  141.                     hdrproc_addr },
  142.     { 'a', "A)ction",        13, HF_DISP_LEFT, action, action, NULL },
  143.     { 'e', "E)xpires",        13, HF_DISP_RIGHT|HF_PROMPT_EXP, expires_days,
  144.                     expires, hdrproc_expires },
  145.     { 'p', "P)riority",        14, HF_DISP_LEFT, priority, priority, NULL },
  146.     { 'n', "Precede(n)ce",    14, HF_DISP_RIGHT, precedence,
  147.                     precedence, hdrproc_precedence },
  148.     { 'i', "I)n-reply-to",    15, HF_DISP_2ROW,
  149.                     in_reply_to, in_reply_to, NULL },
  150.     { 'u', NULL,        17, HF_DISP_1ROW|HF_PROMPT_USR,
  151.                     user_defined_header,
  152.                     user_defined_header, hdrproc_userhdr },
  153.     { -1, NULL, -1, -1, NULL, NULL, NULL },
  154. };
  155.  
  156. /*
  157.  * Selection of individual fields.  The indices *must* correspond
  158.  * to the above "hmenu_item_list[]" list.
  159.  */
  160. #define hmenu_to        (hmenu_item_list[0])
  161. #define hmenu_cc        (hmenu_item_list[1])
  162. #define hmenu_bcc        (hmenu_item_list[2])
  163. #define hmenu_subject        (hmenu_item_list[3])
  164. #define hmenu_replyto        (hmenu_item_list[4])
  165. #define hmenu_action        (hmenu_item_list[5])
  166. #define hmenu_expires        (hmenu_item_list[6])
  167. #define hmenu_priority        (hmenu_item_list[7])
  168. #define hmenu_precedence    (hmenu_item_list[8])
  169. #define hmenu_inreplyto        (hmenu_item_list[9])
  170. #define hmenu_userdef        (hmenu_item_list[10])
  171.  
  172.  
  173. edit_headers()
  174. {
  175.     int c, i, do_redraw;
  176.     struct hdr_menu_item *h;
  177.     
  178.     /* expand out all of the header values */
  179.     /* menu displays expanded values, user edits unexpended versions */
  180.     for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
  181.     if (h->hdrproc != NULL)
  182.         (*h->hdrproc)(h);
  183.     }
  184.  
  185.     clearerr(stdin);
  186.     do_redraw = TRUE;
  187.     while (TRUE) {    /* forever */
  188.  
  189.     /* redraw the entire display if required */
  190.     if (do_redraw) {
  191.         ClearScreen();
  192.         Centerline(0, catgets(elm_msg_cat, ElmSet,
  193.         ElmHdrmenuScreenTitle, "Message Header Edit Screen"));
  194.         for (h = hmenu_item_list ; h->menucmd > 0 ; ++h)
  195.         hdrmenu_put(h, TRUE);
  196.         do_redraw = FALSE;
  197.     }
  198.  
  199.     /* display the instructions */
  200. #ifdef ALLOW_SUBSHELL
  201.     Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
  202.         ElmHdrmenuInstruct,
  203.         "Choose header, u)ser defined header, d)omainize, !)shell, or <return>."));
  204. #else
  205.     Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
  206.         ElmHdrmenuInstructNoShell,
  207.         "Choose header, u)ser defined header, d)omainize, or <return>."));
  208. #endif
  209.  
  210.     /* prompt for command */
  211.     PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
  212.         ElmHdrmenuPrompt, "Choice: "));
  213.     c = getchar();
  214.     if (isupper(c))
  215.         c = tolower(c);
  216.     hdrmenu_clear_promptarea();
  217.  
  218.     /* execute the command */
  219.     switch (c) {
  220.  
  221.     case EOF:
  222.     case RETURN:
  223.     case LINE_FEED:
  224.     case 'q':
  225.         return 0;
  226.  
  227.     case 'd':
  228.         if (domainize_submenu() != 0)
  229.         return 0;
  230.         break;
  231.  
  232. #ifdef ALLOW_SUBSHELL
  233.     case '!':
  234.         if (subshell())
  235.         do_redraw = TRUE;
  236.         break;
  237. #endif
  238.  
  239.     case ctrl('L'):
  240.         do_redraw = TRUE;
  241.         break;
  242.     
  243.     default:
  244.         for (h = hmenu_item_list ; h->menucmd > 0 ; ++h) {
  245.         if (h->menucmd == c) {
  246.             if (hdrmenu_get(h) != 0)
  247.             return 0;
  248.             hdrmenu_put(h, FALSE);
  249.             break;
  250.         }
  251.         }
  252.         if (h->menucmd <= 0) {
  253.         Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
  254.             ElmHdrmenuBadChoice, "No such header!"));
  255.         Writechar('\007');
  256.         }
  257.         break;
  258.  
  259.     }
  260.  
  261.     }
  262.  
  263.     /*NOTREACHED*/
  264. }
  265.  
  266.  
  267. /*
  268.  * Erase instructions, user input, left-over errors, etc.
  269.  * This should be run after every user input command in this module.
  270.  */
  271. static void hdrmenu_clear_promptarea()
  272. {
  273.     clear_error();
  274.     MoveCursor(TOPMOST_PROMPTAREA_LINE, 0);
  275.     CleartoEOS();
  276. }
  277.  
  278.  
  279. /*
  280.  * Prompt the user for a header value, and do any required post-processing.
  281.  */
  282. static int hdrmenu_get(h)
  283. struct hdr_menu_item *h;
  284. {
  285.     char *s;
  286.     int plen, ret, do_append;
  287.  
  288.     /* display the instructions */
  289.     switch (h->flags & HF_PROMPT_MASK) {
  290.     case HF_PROMPT_EXP:
  291.     Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
  292.         ElmHdrmenuGetExpiresInstruct,
  293.         "In how many days should this message expire? "));
  294.     break;
  295.     case HF_PROMPT_USR:
  296.     Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
  297.         ElmHdrmenuGetUserdefInstruct,
  298.         "Enter in the format \"HeaderName: HeaderValue\"."));
  299.     break;
  300.     default:
  301.     Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
  302.         ElmHdrmenuGetInstruct, "Enter value for the header."));
  303.     break;
  304.     }
  305.  
  306.     /* display a prompt */
  307.     plen = 0;
  308.     if (h->hdrname != NULL) {
  309.     MoveCursor(INPUT_LINE, 0);
  310.     for (s = h->hdrname ; *s != '\0' ; ++s) {
  311.         if (*s != '(' && *s != ')') {
  312.         Writechar(*s);
  313.         ++plen;
  314.         }
  315.     }
  316.     Writechar(':');
  317.     Writechar(' ');
  318.     plen += 2;
  319.     }
  320.  
  321.     /* get input from the user */
  322.     do_append = ((h->flags & HF_APPENDENTRY) != 0);
  323.     ret = optionally_enter(h->inpval, INPUT_LINE, plen, do_append, FALSE);
  324.     hdrmenu_clear_promptarea();
  325.  
  326.     /* bail out on error */
  327.     if (ret < 0)
  328.     return -1;
  329.  
  330.     /* see if there is some processing required on this value */
  331.     if (h->hdrproc != NULL)
  332.     (void) (*h->hdrproc)(h);
  333.  
  334.     return 0;
  335. }
  336.  
  337.  
  338. /*
  339.  * Dispay a header and its value in the appropriate field.
  340.  */
  341. static void hdrmenu_put(h, already_clear)
  342. struct hdr_menu_item *h;
  343. int already_clear;
  344. {
  345.     char    *p;
  346.     int     start_row, max_row, start_col, max_col, row, col;
  347.  
  348.     /* figure out the dimensions of the field */
  349.     switch (h->flags & HF_DISP_MASK) {
  350.     case HF_DISP_LEFT:
  351.     start_row = h->lineno;        max_row = h->lineno;
  352.     start_col = 0;            max_col = COLUMNS/2 - 2;
  353.     break;
  354.     case HF_DISP_RIGHT:
  355.     start_row = h->lineno;        max_row = h->lineno;
  356.     start_col = COLUMNS/2 + 1;    max_col = COLUMNS-1;
  357.     break;
  358.     case HF_DISP_3ROW:
  359.     start_row = h->lineno;        max_row = h->lineno+2;
  360.     start_col = 0;            max_col = COLUMNS-1;
  361.     break;
  362.     case HF_DISP_2ROW:
  363.     start_row = h->lineno;        max_row = h->lineno+1;
  364.     start_col = 0;            max_col = COLUMNS-1;
  365.     break;
  366.     default:
  367.     start_row = h->lineno;        max_row = h->lineno;
  368.     start_col = 0;            max_col = COLUMNS-1;
  369.     break;
  370.     }
  371.  
  372.     /* display the header name */
  373.     MoveCursor(start_row, start_col);
  374.     if (h->hdrname != NULL) {
  375.     for (p = h->hdrname ; *p != '\0' ; ++p)
  376.         Writechar(*p);
  377.     Writechar(':');
  378.     Writechar(' ');
  379.     }
  380.  
  381.     /* display the header value */
  382.     GetXYLocation(&row, &col);
  383.     for (p = h->expval ; *p != '\0' && row <= max_row ; ++p) {
  384.     if (row == max_row && col == max_col-4 && strlen(p) > 4)
  385.         p = " ...";        /* neat hack alert */
  386.     Writechar(*p);
  387.     if (!isprint(*p) || ++col > max_col)
  388.         GetXYLocation(&row, &col);
  389.     }
  390.  
  391.     /* save some drawing if we know the screen is already empty */
  392.     if (!already_clear) {
  393.  
  394.     /* clear out remaining space in this line of the field */
  395.     if (max_col == COLUMNS-1) {
  396.         /* people on slow terminals might appreciate doing it this way */
  397.         CleartoEOLN();
  398.     } else {
  399.         while (col++ <= max_col)
  400.         Writechar(' ');
  401.     }
  402.  
  403.     /* for multi-line fields, clear out any unused lines */
  404.     /* this assumes that multi-line fields span the entire screen width */
  405.     while (++row <= max_row) {
  406.         /* grrrrrr -- this is a multi-statement macro */
  407.         ClearLine(row);
  408.     }
  409.  
  410.     }
  411.  
  412. }
  413.  
  414.  
  415. /*
  416.  * Process the to, cc, and bcc headers.  The value entered by the
  417.  * user is expanded.  A successful status is always returned.
  418.  */
  419. static int hdrproc_addr(h)
  420. struct hdr_menu_item *h;
  421. {
  422.     (void) build_address(strip_commas(h->inpval), h->expval);
  423.     return 0;
  424. }
  425.  
  426.  
  427. /*
  428.  * Process the expires header.  The value entered by the user is interpreted
  429.  * as a number of days, and is expanded out to a date specification.  If
  430.  * an error occurs a message is printed, the expanded value is cleared
  431.  * out, and a -1 is returned.
  432.  */
  433. static int hdrproc_expires(h)
  434. struct hdr_menu_item *h;
  435. {
  436.     int days;
  437.  
  438.     /* initialize expanded date spec to empty */
  439.     h->expval[0] = '\0';
  440.  
  441.     /* blank is ok */
  442.     if (h->inpval[0] == '\0')
  443.     return 0;
  444.  
  445.     /* verify the number of days is valid and in range */
  446.     days = atoi(h->inpval);
  447.     if (days < 1) {
  448.     Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
  449.         ElmHdrmenuExpiresNotNumber,
  450.         "Expiration must be specified as a number of days."));
  451.     return -1;
  452.     }
  453.     if (days > 8*7) {
  454.     Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
  455.         ElmHdrmenuExpiresOutOfRange,
  456.         "Expiration date must be within eight weeks of today."));
  457.     return -1;
  458.     }
  459.  
  460.     /* convert number of days to a date */
  461.     days_ahead(days, h->expval);
  462.     return 0;
  463. }
  464.  
  465.  
  466. /*
  467.  * Process the precedence header.  The value entered by the user is
  468.  * checked against the list of allowed precedences, if one exists.  If
  469.  * the precedence has a priority assigned to it, then an empty priority
  470.  * field will be filled in with that value.  If an error occurs a message
  471.  * is printed, the precedence value is cleared out, and a -1 is returned.
  472.  */
  473. static int hdrproc_precedence(h)
  474. struct hdr_menu_item *h;
  475. {
  476.     char buf[SLEN];    /* assumes sizeof(allowed_precedences) <= SLEN */
  477.     char *bp, *prec, *prio;
  478.  
  479.     /* empty is ok */
  480.     if (h->inpval[0] == '\0')
  481.     return 0;
  482.  
  483.     /* if there are no restrictions on precedence then anything is ok */
  484.     if (allowed_precedences[0] == '\0')
  485.     return 0;
  486.  
  487.     /* the "allowed_precedences[]" format is: */
  488.     /*   precedence[:priority-value] precedence[:priority-value] ... */
  489.     bp = strcpy(buf, allowed_precedences);
  490.     while ((prec = strtok(bp, " \t\n")) != NULL) {
  491.     bp = NULL;
  492.     if ((prio = index(prec, ':')) != NULL)
  493.         *prio++ = '\0';
  494.     if (istrcmp(prec, h->inpval) == 0)
  495.         break;
  496.     }
  497.  
  498.     /* see if we reached the end of the list without a match */
  499.     if (prec == NULL) {
  500.     Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
  501.         ElmHdrmenuPrecedenceBadValue,
  502.         "Unknown precedence value specified."));
  503.     h->inpval[0] = '\0';
  504.     return -1;
  505.     }
  506.  
  507.     /* see if this precedence has an associated priority */
  508.     if (prio != NULL && hmenu_priority.inpval[0] == '\0') {
  509.     (void) strcpy(hmenu_priority.inpval, prio);
  510.     hdrmenu_put(&hmenu_priority, FALSE);
  511.     }
  512.  
  513.     return 0;
  514. }
  515.  
  516.  
  517. /*
  518.  * Process the user-defined header.  The value entered by the user is
  519.  * verified for proper format.  If an error occurs a message is printed,
  520.  * the expanded value is cleared out, and a -1 is returned.
  521.  */
  522. static int hdrproc_userhdr(h)
  523. struct hdr_menu_item *h;
  524. {
  525.     char *s;
  526.  
  527.     /* empty is ok */
  528.     if (h->inpval[0] == '\0')
  529.     return 0;
  530.  
  531.     /* make sure the header name doesn't begin with some strange character */
  532.     if (!isalnum(h->inpval[0])) {
  533.     Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
  534.         ElmHdrmenuUserdefNotAlnum,
  535.         "The user-defined header must begin with a letter or number."));
  536.     h->inpval[0] = '\0';
  537.     return -1;
  538.     }
  539.  
  540.     /* locate the end of the header name */
  541.     for (s = h->inpval ; *s != ':' && isprint(*s) && !isspace(*s) ; ++s)
  542.     ;
  543.  
  544.     /* there needs to be a colon at the end of the header name */
  545.     if (*s != ':') {
  546.     Centerline(ERROR_LINE, catgets(elm_msg_cat, ElmSet,
  547.         ElmHdrmenuUserdefMissingColon,
  548.         "The user-defined header must have a colon after the field name."));
  549.     h->inpval[0] = '\0';
  550.     return -1;
  551.     }
  552.  
  553.     return 0;
  554. }
  555.  
  556.  
  557. /*
  558.  * Prompt the user to domainize a header.
  559.  */
  560. static int domainize_submenu()
  561. {
  562.     int c;
  563.     struct hdr_menu_item *h;
  564.  
  565.     Centerline(INSTRUCT_LINE, catgets(elm_msg_cat, ElmSet,
  566.     ElmHdrmenuDomInstruct,
  567.     "Select header to domainize:  T)o, C)c, B)cc, or <return>."));
  568.     PutLine0(INPUT_LINE, 0, catgets(elm_msg_cat, ElmSet,
  569.     ElmHdrmenuDomPrompt, "Domainize choice: "));
  570.  
  571.     for (;;) {
  572.  
  573.     c = getchar();
  574.  
  575.     switch ((int)(isupper(c) ? tolower(c) : c)) {
  576.     case 't':
  577.         h = &hmenu_to;
  578.         break;
  579.     case 'c':
  580.         h = &hmenu_cc;
  581.         break;
  582.     case 'b':
  583.         h = &hmenu_bcc;
  584.         break;
  585.     case '\r':
  586.     case '\n':
  587.     case EOF:
  588.         h = NULL;
  589.         break;
  590.     default:
  591.         Writechar('\007');
  592.         continue;
  593.     }
  594.  
  595.     if (h != NULL) {
  596.         domainize(h->expval);
  597.         hdrmenu_put(h, FALSE);
  598.     }
  599.  
  600.     hdrmenu_clear_promptarea();
  601.     return (c == EOF ? -1 : 0);
  602.  
  603.     }
  604.  
  605.     /*NOTREACHED*/
  606. }
  607.  
  608.  
  609.  
  610. static void domainize(addresses)
  611. char *addresses;
  612. {
  613.     /*** Convert the given addresses from bang paths to domain format.
  614.          This policy amounts to Rabid Rerouting.  However, since it's
  615.          under the sender's control, I don't mind. ***/
  616.  
  617.     char buffer[VERY_LONG_STRING];
  618.     char *a, *d;
  619.     int how_many;
  620.  
  621.     strcpy(buffer, addresses);
  622.     a = buffer;
  623.     d = addresses;
  624.     how_many = 0;
  625.  
  626.     for (;;) {
  627.       while (*a == ' ' || *a == ',')
  628.         ++a;
  629.       if (*a == '\0')
  630.         break;
  631.  
  632.       if (*a == '(' || *a == '"') {
  633.         int endch;
  634.  
  635.         if (d != addresses)
  636.           *d++ = ' ';
  637.         endch = (*a == '(') ? ')' : '"';
  638.         while (*a && *a != endch) {
  639.           if (*a == '\\' && *(a + 1))
  640.         *d++ = *a++;
  641.           *d++ = *a++;
  642.         }
  643.         if (*a)
  644.           *d++ = *a++;
  645.       }
  646.       else {
  647.         char *addr;
  648.  
  649.         if (how_many) {
  650.           *d++ = ',';
  651.           *d++ = ' ';
  652.         }
  653.         ++how_many;
  654.  
  655.         if (*a == '<') {
  656.           *d++ = *a++;
  657.           addr = a;
  658.           while (*a && *a != '>') {
  659.         if (*a == '\\' && *(a + 1))
  660.           ++a;
  661.         ++a;
  662.           }
  663.           if (*a)
  664.         *a++ = '\0';
  665.           domainize_addr(addr, d);
  666.           d += strlen(d);
  667.           *d++ = '>';
  668.         }
  669.         else {
  670.           addr = a;
  671.           while (*a && *a != ' ' && *a != ',')
  672.         ++a;
  673.           if (*a)
  674.         *a++ = '\0';
  675.           domainize_addr(addr, d);
  676.           d += strlen(d);
  677.         }
  678.       }
  679.     }
  680.  
  681.     *d = '\0';
  682. }
  683.  
  684. static void domainize_addr(src, dest)
  685. char *src, *dest;
  686. {
  687.     /*** Convert one address to domain form. ***/
  688.  
  689.     char *locpart, *host;
  690.  
  691.     if (index(src, '@') != NULL
  692.      || (locpart = rindex(src, '!')) == NULL) {
  693.       strcpy(dest, src);
  694.       return;
  695.     }
  696.  
  697.     *locpart++ = '\0';
  698.  
  699.     if ((host = rindex(src, '!')) != NULL)
  700.       ++host;
  701.     else
  702.       host = src;
  703.     sprintf(dest, "%s@%s", locpart, host);
  704.     if (!index(host, '.'))
  705.       strcat(dest, ".uucp");
  706.  
  707.     *--locpart = '!';
  708. }
  709.  
  710.